home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.2a2 / WEDrawing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-12  |  27.9 KB  |  1,024 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEDrawing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing routines and other basic support functions
  6.  *
  7.  *  Copyright (c) 1993-1995 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14. #include "WASTEIntf.h"
  15.  
  16. #ifndef __PALETTES__
  17. #include <Palettes.h>
  18. #endif
  19.  
  20. #ifndef __QDOFFSCREEN__
  21. #include <QDOffscreen.h>
  22. #endif
  23.  
  24. pascal long WEOffsetToLine (long offset, WEHandle hWE)
  25. {
  26.     // given a byte offset into the text, find the corresponding line index
  27.  
  28.     WEPtr pWE = *hWE;
  29.     LineArrayPtr pLines = *pWE->hLines;
  30.     long minIndex, maxIndex, index;
  31.  
  32.     // do a fast binary search through the line array
  33.     minIndex = 0;
  34.     maxIndex = pWE->nLines;
  35.  
  36.     while (minIndex < maxIndex)
  37.     {
  38.         index = (minIndex + maxIndex) >> 1;
  39.         if (offset >= pLines[index].lineStart) 
  40.         {
  41.             if (offset < pLines[index + 1].lineStart) 
  42.             {
  43.                 break;
  44.             }
  45.             else
  46.             {
  47.                 minIndex = index + 1;
  48.             }
  49.         }
  50.         else
  51.         {
  52.             maxIndex = index;
  53.         }
  54.     }
  55.  
  56.     return index;
  57. }
  58.  
  59. pascal long _WEPixelToLine(long vOffset, WEHandle hWE)
  60. {
  61.     // given a vertical pixel offset in local coordinates,
  62.     // find the corresponding line index
  63.  
  64.     WEPtr pWE = *hWE;
  65.     LineArrayPtr pLines = *pWE->hLines;
  66.     long minIndex, maxIndex, index;
  67.  
  68.     // do a fast binary search through the line array
  69.     minIndex = 0;
  70.     maxIndex = pWE->nLines;
  71.  
  72.     while (minIndex < maxIndex)
  73.     {
  74.         index = (minIndex + maxIndex) >> 1;
  75.         if (vOffset >= pLines[index].lineOrigin) 
  76.         {
  77.             if (vOffset < pLines[index + 1].lineOrigin) 
  78.             {
  79.                 break;
  80.             }
  81.             else
  82.             {
  83.                 minIndex = index + 1;
  84.             }
  85.         }
  86.         else
  87.         {
  88.             maxIndex = index;
  89.         }
  90.     }
  91.     
  92.     return index;
  93. }
  94.  
  95. pascal long _WEOffsetToRun (long offset, WEHandle hWE)
  96. {
  97.     // given a byte offset into the text, find the corresponding style run index
  98.  
  99.     WEPtr pWE = *hWE;
  100.     RunArrayPtr pRuns = *pWE->hRuns;
  101.     long minIndex, maxIndex, index;
  102.  
  103.     // do a fast binary search through the style run array
  104.     minIndex = 0;
  105.     maxIndex = pWE->nRuns;
  106.  
  107.     while (minIndex < maxIndex)
  108.     {
  109.         index = (minIndex + maxIndex) >> 1;
  110.         if (offset >= pRuns[index].runStart)
  111.         { 
  112.             if (offset < pRuns[index + 1].runStart) 
  113.             {
  114.                 break;
  115.             }
  116.             else
  117.             {
  118.                 minIndex = index + 1;
  119.             }
  120.         }
  121.         else
  122.         {
  123.             maxIndex = index;
  124.         }
  125.     }
  126.  
  127.     return index;
  128. }
  129.  
  130. pascal void _WEGetIndStyle(long runIndex, WERunInfo *info, WEHandle hWE)
  131. {
  132.     WEPtr pWE = *hWE;    // assume WE record is already locked
  133.     RunArrayElementPtr pRun;
  134.  
  135.     // get a pointer to the specified run array element
  136.     pRun = *pWE->hRuns + runIndex;
  137.  
  138.     // fill in the runStart and runEnd fields from the style run array
  139.     info->runStart = pRun[0].runStart;
  140.     info->runEnd = pRun[1].runStart;
  141.  
  142.     // copy the style information from the appropriate entry in the style table
  143.     info->runAttrs = (*pWE->hStyles)[pRun->styleIndex].info;
  144. }
  145.  
  146. pascal void WEGetRunInfo(long offset, WERunInfo *info, WEHandle hWE)
  147. {
  148.     _WEGetIndStyle(_WEOffsetToRun(offset, hWE), info, hWE);
  149. }
  150.  
  151. #if WASTE_OBJECTS
  152.  
  153. pascal OSErr WEGetSelectedObject(WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  154. {
  155.     WEPtr pWE = *hWE;
  156.     WERunInfo runInfo;
  157.  
  158.     // assume current selection is not an embedded object
  159.     *hObjectDesc = NULL;
  160.  
  161.     // check selection range
  162.     if (pWE->selEnd - pWE->selStart == 1)
  163.     {
  164.         // check run info
  165.         WEGetRunInfo(pWE->selStart, &runInfo, hWE);
  166.         if ((*hObjectDesc = runInfo.runAttrs.runStyle.tsObject) != NULL)
  167.             return noErr;
  168.     }
  169.     return weObjectNotFoundErr;
  170. } // WEGetSelectedObject
  171.  
  172. pascal long WEFindNextObject(long offset, WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  173. {
  174.     WEPtr pWE = *hWE;
  175.     StyleTablePtr pStyles = *pWE->hStyles;
  176.     WEObjectDescHandle obj;
  177.     RunArrayElementPtr pRun;
  178.     
  179.     *hObjectDesc = NULL;
  180.  
  181.     // do nothing if offset is already at the end of the text
  182.     if (offset >= pWE->textLength - 1)
  183.         return kInvalidOffset;
  184.  
  185.     // get a pointer to the run array element immediately following offset
  186.     pRun = *pWE->hRuns + _WEOffsetToRun(offset + 1, hWE);
  187.  
  188.     // perform a linear scan of the run array looking for a run whose
  189.     // corresponding style table entry points to an embedded object;
  190.     // the search will stop anyway because the last run array element has styleIndex = -1
  191.     while (pRun->styleIndex >= 0)
  192.     {
  193.         if ((obj = pStyles[pRun->styleIndex].info.runStyle.tsObject) != NULL)
  194.         {
  195.             *hObjectDesc = obj;
  196.             return pRun->runStart;
  197.         }
  198.         pRun++;
  199.     }
  200.  
  201.     return kInvalidOffset;
  202.  
  203. } // WEFindNextObject
  204.  
  205. #endif  // WASTE_OBJECTS
  206.  
  207. pascal void _WEContinuousStyleRange(long rangeStart, long rangeEnd, short *mode,
  208.         WETextStyle *ts, WEHandle hWE)
  209. {
  210.     // find out which style attributes are continous over the specified text range
  211.     // on entry, *mode specifies which attributes are to be checked
  212.     // on exit, *mode specifies the continuous attributes, also copied to ts
  213.  
  214.     WEPtr pWE = *hWE;
  215.     long bitmap;
  216.     long runIndex;
  217.     WERunInfo runInfo;
  218.  
  219.     // get bitmap of style attributes to check (masking out private and unused bits)
  220.     bitmap = *mode & weDoAll;
  221.  
  222.     // get style info at the beginning of the specified range
  223.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  224.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  225.  
  226.     // copy the specified fields to ts
  227.     _WECopyStyle(&runInfo.runAttrs.runStyle, (WETextStyle *) ts, 0, bitmap | weDoReplaceFace);
  228.  
  229.     // loop through style runs across the current selection range
  230.     // if we determine that all specified attributes are discontinuous, we exit prematurely
  231.     do
  232.     {
  233.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  234.  
  235.         // determine which attributes have changed, if any
  236.         if (bitmap & weDoFont)
  237.         { 
  238.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  239.             {
  240.                 bitmap &= (~weDoFont);
  241.             }
  242.         }
  243.         if (bitmap & weDoFace)
  244.         { 
  245.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  246.             { 
  247.                 ts->tsFace &= runInfo.runAttrs.runStyle.tsFace;
  248.                 if (ts->tsFace == 0)
  249.                 {
  250.                     bitmap &= (~weDoFace);
  251.                 }
  252.             }
  253.         }
  254.         if (bitmap & weDoSize)
  255.         { 
  256.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize) 
  257.             {
  258.                 bitmap &= (~weDoSize);
  259.             }
  260.         }
  261.         if (bitmap & weDoColor)
  262.         { 
  263.             if (!_WEBlockCmp(&runInfo.runAttrs.runStyle.tsColor, &ts->tsColor, sizeof(RGBColor)))
  264.             {
  265.                 bitmap &= (~weDoColor);
  266.             }
  267.         }
  268.  
  269.         runIndex++;
  270.     } while ((bitmap != 0) && (runInfo.runEnd < rangeEnd));
  271.  
  272.     *mode = bitmap;
  273. }
  274.  
  275. pascal void _WESynchNullStyle(WEHandle hWE)
  276. {
  277.     // This routine fills the nullStyle field of the WE record with valid information
  278.     // and makes sure that the null style font belongs to the keyboard script.
  279.  
  280.     WEPtr pWE = *hWE;    // assume WE record is already locked
  281.     long runIndex;
  282.     WERunInfo runInfo;
  283. #if !WASTE_NO_SYNCH
  284.     ScriptCode keyboardScript;
  285.     short fontID;
  286. #endif
  287.  
  288.     // find the run index of the style run preceding the insertion point
  289.     runIndex = _WEOffsetToRun(pWE->selStart - 1, hWE);
  290.  
  291.     // if the nullStyle record is marked as invalid, fill it with the style attributes
  292.     // associated with the character preceding the insertion point, and mark it as valid
  293.     if (!BTST(pWE->flags, weFUseNullStyle)) 
  294.     {
  295.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  296.         pWE->nullStyle = runInfo.runAttrs;
  297.         BSET(pWE->flags, weFUseNullStyle);
  298.     }
  299.  
  300. #if !WASTE_NO_SYNCH
  301.     // if only the Roman script is installed, we're finished
  302.     if (!BTST(pWE->flags, weFNonRoman)) 
  303.     {
  304.         return;
  305.     }
  306.  
  307.     // *** FONT / KEYBOARD SYNCHRONIZATION ***
  308.     // get the keyboard script
  309.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  310.  
  311.     // find out what font will be used for the next character typed
  312.     fontID = pWE->nullStyle.runStyle.tsFont;
  313.  
  314.     // do nothing if the font script is the same as the keyboard script
  315.     if (FontToScript(fontID) == keyboardScript) return; 
  316.  
  317.     //{ scan style runs starting from the insertion point backwards,}
  318.     // looking for the first font belonging to the keyboard script
  319.     do
  320.     {
  321.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  322.         fontID = runInfo.runAttrs.runStyle.tsFont;
  323.         if (FontToScript(fontID) == keyboardScript) break;
  324.         runIndex--;
  325.     } while (runIndex>=0);
  326.     
  327.     // if no font was ever used for the keyboard script, default to the
  328.     // application font for the script
  329.     if (runIndex < 0) 
  330.     {
  331.         fontID = GetScriptVariable(keyboardScript, smScriptAppFond);
  332.     }
  333.     
  334.     // change the font in the null style record
  335.     pWE->nullStyle.runStyle.tsFont = fontID;
  336. #endif
  337. }
  338.  
  339. pascal Boolean WEContinuousStyle (short *mode, TextStyle *ts, WEHandle hWE)
  340. {
  341.     // find out which style attributes are continous over the selection range
  342.     // on entry, the mode bitmap specifies which attributes are to be checked
  343.     // on exit, the mode bitmap specifies the continuous attributes, also copied to ts
  344.     // return TRUE if all specified attributes are continuous
  345.  
  346.     WEPtr pWE;
  347.     short oldMode;
  348.     Boolean continuousStyle;
  349.     Boolean saveWELock;
  350.  
  351.     // lock the WE record
  352.     
  353.     pWE = *hWE;
  354.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  355.  
  356.     // mask out private and unused bits in mode so we don't run the risk of overwriting
  357.     // memory past the ts record (which is defined as TextStyle in the public interfaces)
  358.     *mode = *mode & weDoAll;
  359.  
  360.     // two rather different paths are taken depending on whether
  361.     // the selection range is empty or not
  362.     if (pWE->selStart == pWE->selEnd) 
  363.     {
  364.         // if the selection range is empty, always return TRUE and set ts
  365.         // from the nullStyle record, after having validated it
  366.         continuousStyle = true;
  367.         _WESynchNullStyle(hWE);
  368.         _WECopyStyle(&pWE->nullStyle.runStyle, (WETextStyle *)ts, 0, *mode | weDoReplaceFace);
  369.     }
  370.     else
  371.     {
  372.         // otherwise get the continuous style attributes over the selection range
  373.         oldMode = *mode;
  374.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *)ts, hWE);
  375.  
  376.         // return TRUE if mode hasn't changed
  377.         continuousStyle = (oldMode == *mode);
  378.     }
  379.  
  380.     // unlock the WE record
  381.     _WESetHandleLock((Handle) hWE, saveWELock);    
  382.  
  383.     return continuousStyle;
  384. }
  385.  
  386. pascal void _WESegmentLoop(long firstLine, long lastLine, SegmentLoopProcPtr callback, void *callbackData,
  387.         WEHandle hWE)
  388. {
  389.     // For each style segment on every line in the specified range, set up
  390.     // text attributes in the port and call the callback.
  391.     // the WE record must be already locked
  392.  
  393.     WEPtr pWE = *hWE;
  394.     LinePtr pLine;
  395.     Ptr pText;
  396.     long lineIndex;
  397.     long runIndex, previousRunIndex;
  398.     long lineStart, lineEnd, segmentStart, segmentEnd;
  399.     JustStyleCode styleRunPosition;
  400.     WERunInfo runInfo;
  401.     Boolean saveLineLock;
  402.     Boolean saveTextLock;
  403.     QDEnvironment saveEnvironment;
  404.  
  405.     // save the QuickDraw environment
  406.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD) ? true : false, &saveEnvironment);
  407.  
  408.     // make sure firstLine and lastLine are within the allowed range
  409.     lineIndex = pWE->nLines - 1;
  410.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  411.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  412.  
  413.     // lock the line array
  414.     saveLineLock = _WESetHandleLock((Handle) pWE->hLines, true);
  415.     pLine = *pWE->hLines + firstLine;
  416.  
  417.     // lock the text
  418.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  419.     pText = *pWE->hText;
  420.  
  421.     // find the style run index corresponding to the beginning of the first line
  422.     runIndex = _WEOffsetToRun(pLine->lineStart, hWE);
  423.     previousRunIndex = -1;
  424.  
  425.     // loop thru the specified lines
  426.     for ( lineIndex = firstLine; lineIndex <= lastLine; lineIndex++ )
  427.     {
  428.         // get line start and line end
  429.         lineStart = pLine[0].lineStart;
  430.         lineEnd = pLine[1].lineStart;
  431.  
  432.         // loop thru each style run on this line
  433.         do
  434.         {
  435.             // get style run information for the current style run
  436.             _WEGetIndStyle(runIndex, &runInfo, hWE);
  437.  
  438.             if (previousRunIndex != runIndex) 
  439.             {
  440.                 // set new text attributes
  441.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  442.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  443.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  444.  
  445.                 // remember previous run index
  446.                 previousRunIndex = runIndex;
  447.             }
  448.  
  449.             // determine the relative position of this style run on the line
  450.             styleRunPosition = 0;                    // onlyStyleRun
  451.  
  452.             if (runInfo.runStart <= lineStart)
  453.             { 
  454.                 segmentStart = lineStart;
  455.             }
  456.             else
  457.             {
  458.                 styleRunPosition += 2;                // rightStyleRun or middleStyleRun
  459.                 segmentStart = runInfo.runStart;
  460.             }
  461.  
  462.             if (runInfo.runEnd >= lineEnd)
  463.             {
  464.                         segmentEnd = lineEnd;
  465.             }
  466.             else
  467.             {
  468.                 styleRunPosition += 1;                // leftStyleRun or middleStyleRun
  469.                 segmentEnd = runInfo.runEnd;
  470.             }
  471.  
  472.             // do the callback
  473.             if ((callback)(pLine, &runInfo.runAttrs, pText + segmentStart,
  474.                     segmentStart, segmentEnd - segmentStart, styleRunPosition, callbackData))
  475.             {
  476.                 break;
  477.             }
  478.  
  479.             // advance style run index, unless this style run goes on to the next line
  480.             if (runInfo.runEnd <= lineEnd)
  481.             { 
  482.                 runIndex++;
  483.             }
  484.         } while (runInfo.runEnd < lineEnd);
  485.         
  486.         pLine++;
  487.  
  488.     }  // for
  489.  
  490.     // unlock the text
  491.     _WESetHandleLock(pWE->hText, saveTextLock);    
  492.  
  493.     // unlock the line array
  494.     _WESetHandleLock((Handle) pWE->hLines, saveLineLock);
  495.  
  496.     // restore the QuickDraw environment
  497.     _WERestoreQDEnvironment(&saveEnvironment);
  498. }
  499.  
  500. pascal void _WEDrawTSMHilite(Rect *segmentRect, short tsFlags)
  501. {
  502.     short underlineHeight;
  503.     RGBColor background, foreground, saveForeground;
  504.     Boolean isColorPort;
  505.     Boolean usingTrueGray;
  506.  
  507.     isColorPort = (((CGrafPtr)(qd.thePort))->portVersion < 0);
  508.     usingTrueGray = false;
  509.  
  510.     // by default, the pen pattern is solid
  511.     PenPat(&qd.black);
  512.  
  513.     // if we're drawing in color, set the foreground color
  514.     if (isColorPort) 
  515.     {
  516.         // save foreground color
  517.         GetForeColor(&saveForeground);
  518.  
  519.         // by default, the foreground color is black
  520.         foreground.red = 0;
  521.         foreground.green = 0;
  522.         foreground.blue = 0;
  523.  
  524.         // if we're underlining raw (unconverted) text, see if a "true gray" is available
  525.         if (!BTST(tsFlags, tsTSMConverted)) 
  526.         {
  527.             GetBackColor(&background);
  528.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  529.         } // if raw text
  530.  
  531.         // set the foreground color
  532.         RGBForeColor(&foreground);
  533.     } // if color graf port
  534.  
  535.     // if we're underlining raw (unconverted) text and no true gray is available,
  536.     // simulate gray with a 50% pattern
  537.     if (!BTST(tsFlags, tsTSMConverted)) 
  538.     {
  539.         if (usingTrueGray == false)
  540.         { 
  541.                 PenPat(&qd.gray);
  542.         }
  543.     }
  544.     // use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline
  545.     underlineHeight = BTST(tsFlags, tsTSMSelected) ? 2 : 1;
  546.     
  547.     // segmentRect becomes the rectangle to paint
  548.     InsetRect(segmentRect, 1, 0);
  549.     segmentRect->top = segmentRect->bottom - underlineHeight;
  550.  
  551.     // draw the underline
  552.     PaintRect(segmentRect);
  553.  
  554.     // restore the foreground color
  555.     if (isColorPort) 
  556.     {
  557.         RGBForeColor(&saveForeground);
  558.     }
  559. }
  560.  
  561. static Boolean SLDraw (LinePtr pLine, const WERunAttributes *pAttrs,
  562.         Ptr pSegment, long segmentStart, long segmentLength,
  563.         JustStyleCode styleRunPosition, void *callbackData);
  564.  
  565. static Boolean SLDraw (LinePtr pLine, const WERunAttributes *pAttrs,
  566.         Ptr pSegment, long segmentStart, long segmentLength,
  567.         JustStyleCode styleRunPosition, void *callbackData)
  568. {
  569. #pragma unused(segmentStart)
  570.     struct SLDrawData *p = (struct SLDrawData *) callbackData;
  571.     WEPtr pWE = *p->hWE;
  572.     Fixed slop;
  573.     Rect segmentRect;
  574.     RGBColor theColorBlack;
  575.  
  576.     // is this the first segment on this line?
  577.     if (IS_FIRST_RUN(styleRunPosition))
  578.     {
  579.         // calculate the line rectangle (the rectangle which completely encloses the current line)
  580.         (p->lineRect).left = pWE->destRect.left;
  581.         (p->lineRect).right = pWE->destRect.right;
  582.         (p->lineRect).top = pWE->destRect.top + pLine[0].lineOrigin;
  583.         (p->lineRect).bottom = pWE->destRect.top + pLine[1].lineOrigin;
  584.  
  585.         // calculate the visible portion of this rectangle
  586.         // we do this by intersecting the line rectangle with the view rectangle
  587.         p->drawRect = (*pWE->viewRgn)->rgnBBox;
  588.         if (SectRect(&(p->lineRect), &(p->drawRect), &(p->drawRect)))
  589.         { 
  590.             ;
  591.         }
  592.         if (p->usingOffscreen) 
  593.         {
  594.             // calculate the boundary rectangle for the offscreen buffer
  595.             // this is simply drawRect converted to global coordinates
  596.             p->bounds = p->drawRect;
  597.             LocalToGlobal((Point *) &p->bounds.top);
  598.             LocalToGlobal((Point *) &p->bounds.bottom);
  599.  
  600.             // update the offscreen graphics world for the new bounds (this could fail)
  601.             p->drawingOffscreen = false;
  602.             if (UpdateGWorld((GWorldPtr *) (&pWE->offscreenPort), 0, &p->bounds, (CTabHandle) NULL,
  603.                 (GDHandle) NULL, (GWorldFlags) 0) >= 0)
  604.             {
  605.                 // NOTE: when running on a 68000 machine with the original QuickDraw,
  606.                 // a GWorld is just an extended GrafPort, and GetGWorldPixMap actually
  607.                 // returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap).
  608.                 // An important side-effect of this is that when we call SetOrigin,
  609.                 // only the original portBits is offset, not the copy.
  610.                 // get the pixel map associated with the offscreen graphics world
  611.                 p->offscreenPixels = GetGWorldPixMap((GWorldPtr)(pWE->offscreenPort));
  612.  
  613.                 // lock it down
  614.                 if (LockPixels(p->offscreenPixels)) 
  615.                 {
  616.                     // offscreen pixel buffer allocation was successful
  617.                     p->drawingOffscreen = true;
  618.  
  619.                     // switch graphics world
  620.                     SetGWorld((GWorldPtr)(pWE->offscreenPort), NULL);
  621.  
  622.                     // synchronize the coordinate system of the offscreen port with that of the screen port
  623.                     SetOrigin(p->drawRect.left, p->drawRect.top);
  624.  
  625.                     // reset the offscreen clip region
  626.                     ClipRect(&p->drawRect);
  627.                 }
  628.             } // if pixel buffer allocation was successful
  629.         } // if usingOffscreen
  630.  
  631.         // if doErase is TRUE, erase the drawable area before drawing text
  632.         if (p->doErase) 
  633.         {
  634.             EraseRect(&p->drawRect);
  635.         }
  636.         // position the pen
  637.         MoveTo(p->lineRect.left + _WECalcPenIndent(pLine->lineSlop, pWE->alignment),
  638.             p->lineRect.top + pLine->lineAscent);
  639.     } // if first segment on line
  640.  
  641.     // if drawingOffscreen, switch thePort to the offscreen port
  642.     // and synchronize text attributes
  643.     if (p->drawingOffscreen) 
  644.     {
  645.         SetPort(pWE->offscreenPort);
  646.         TextFont(pAttrs->runStyle.tsFont);
  647.         TextFace(pAttrs->runStyle.tsFace);
  648.         TextSize(pAttrs->runStyle.tsSize);
  649.     } // if drawingOffscreen
  650.  
  651.     // get horizontal coordinate of the pen before drawing the segment
  652.     GetPen((Point *)&segmentRect.top);
  653.  
  654.     // set the foreground color
  655.     if (p->usingColor)
  656.     {
  657.         RGBForeColor(&pAttrs->runStyle.tsColor);
  658.     }
  659.  
  660. #if WASTE_OBJECTS
  661.     if (pAttrs->runStyle.tsObject != NULL)
  662.     {
  663.         _WEDrawObject(pAttrs->runStyle.tsObject);
  664.     }
  665.     else
  666. #endif
  667.     {
  668.         slop = 0;
  669.  
  670.         // calculate the "slop" (extra space) for this text segment (justified text only)
  671.         if (pWE->alignment == weJustify) 
  672.         {
  673.             // if this is the last segment on the line, strip trailing spaces
  674.             if (IS_LAST_RUN(styleRunPosition))
  675.             {
  676.                 segmentLength = VisibleLength(pSegment, segmentLength);
  677.             }
  678.             // calculate how much extra space is to be applied to this text segment
  679.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition, 
  680.                 kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  681.  
  682.         } // if alignment = weJustify
  683.  
  684.         // draw the segment
  685.         CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, p->hWE, pWE->drawTextHook);
  686.     }
  687.     
  688.     // get horizontal coordinate of the pen after drawing the segment
  689.     GetPen((Point *)&segmentRect.bottom);
  690.     segmentRect.bottom = p->lineRect.bottom;
  691.  
  692.     // if this segment is in the TSM area, underline it in the appropriate way
  693.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite)) 
  694.     {
  695.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  696.     }
  697.     if (p->drawingOffscreen) 
  698.     {
  699.         if (IS_LAST_RUN(styleRunPosition)) 
  700.         {
  701.             // after drawing offscreen the last segment,
  702.             // prepare to copy the offscreen buffer to video RAM
  703.  
  704.             // first set the graphics world to the screen port
  705.             SetGWorld((CGrafPtr)p->screenPort, p->screenDevice);
  706.  
  707.             // before calling CopyBits, set the foreground color to black to avoid colorization (color only)
  708.             if (p->usingColor) 
  709.             {
  710.                 theColorBlack.red = 0;
  711.                 theColorBlack.green = 0;
  712.                 theColorBlack.blue = 0;
  713.                 RGBForeColor(&theColorBlack);
  714.             }
  715.             
  716.             // copy the offscreen image of the [visible portion of the] line to the screen
  717.             CopyBits(&pWE->offscreenPort->portBits, &p->screenPort->portBits, &p->drawRect,
  718.                     &p->drawRect, srcCopy, (RgnHandle)NULL);
  719.  
  720.             // restore the original offscreen coordinate system and unlock the pixel image
  721.             SetPort(pWE->offscreenPort);
  722.             SetOrigin(0, 0);
  723.             if (p->usingColor)
  724.                 RGBForeColor(&theColorBlack);    // this fixes a bug in Style 1.3
  725.             UnlockPixels(p->offscreenPixels);
  726.  
  727.         } // if last segment
  728.  
  729.         // restore the screen port for _WESegmentLoop
  730.         SetPort(p->screenPort);
  731.     } // if drawingOffscreen
  732.  
  733.     return false;    // keep looping
  734. }
  735.  
  736. pascal void _WEDrawLines (long firstLine, long lastLine, Boolean doErase, WEHandle hWE)
  737. {
  738.     // draw the specified range of lines
  739.     // we can safely assume that the WE record is already locked
  740.     // and the port is already set the pWE->port
  741.  
  742.     WEPtr pWE = *hWE;                    // assume WE record is locked
  743.     Rect bounds;                        // bounds of the offscreen buffer, in global coordinates
  744.     Boolean usingColor;                    // TRUE if we're drawing in color
  745.     Boolean usingOffscreen;                // TRUE if we're using an offscreen port
  746.     Boolean drawingOffscreen;            // TRUE if actually drawing to an offscreen buffer
  747.     struct SLDrawData callbackData;
  748.     GDHandle screenDevice;
  749.     GrafPtr screenPort;
  750.  
  751.     usingOffscreen = false;
  752.     drawingOffscreen = false;
  753.     usingColor = BTST(pWE->flags, weFHasColorQD) ? true : false;
  754.         
  755.     // do nothing if our graphics port is not visible
  756.     if (EmptyRgn(pWE->port->visRgn))
  757.     {
  758.         return;
  759.     }
  760.  
  761.     // save graphics world
  762.     GetGWorld((GWorldPtr *)&screenPort, &screenDevice);
  763.  
  764.     // If doErase is TRUE, we're drawing over old text, so we must erase each line
  765.     // before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw
  766.     // the entire line offscreen and  we copy the image right over the old line,
  767.     // without erasing it, thus achieving a very smooth drawing effect.
  768.  
  769.     if ((doErase) && BTST(pWE->features, weFDrawOffscreen)) 
  770.     {
  771.         // has an offscreen world already been allocated?
  772.         if (pWE->offscreenPort == NULL) 
  773.         {
  774.             GWorldPtr aGWorld; //mf
  775.             // nope,  create one; its bounds are set initially to an arbitrary rectangle
  776.             SetRect(&bounds, 0, 0, 1, 1);
  777.             if (NewGWorld(&aGWorld, 0, &bounds, NULL, NULL,
  778.                 pixPurge + noNewDevice + useTempMem) != noErr)
  779.             { ; }
  780.             (pWE->offscreenPort)=(GrafPtr)aGWorld; //mf
  781.         }
  782.         usingOffscreen = (pWE->offscreenPort != NULL);
  783.     }
  784.  
  785.     callbackData.hWE = hWE;
  786.     callbackData.bounds = bounds;
  787.     callbackData.usingColor = usingColor;
  788.     callbackData.usingOffscreen = usingOffscreen;
  789.     callbackData.drawingOffscreen = drawingOffscreen;
  790.     callbackData.doErase = doErase;
  791.     SetRect(&callbackData.lineRect,0,0,0,0);                //mf
  792.     SetRect(&callbackData.drawRect,0,0,0,0);                //mf
  793.     callbackData.offscreenPixels = NULL;
  794.     callbackData.screenPort = screenPort;
  795.     callbackData.screenDevice = screenDevice;
  796.  
  797.     _WESegmentLoop(firstLine, lastLine, SLDraw, (void *) &callbackData, hWE);
  798.  
  799.     // restore graphics world
  800.     SetGWorld((GWorldPtr)screenPort, screenDevice);
  801.  
  802. }
  803.  
  804. pascal short _WECalcPenIndent(short slop, short alignment)
  805. {
  806.     short retval;
  807.  
  808.     // if alignment is weFlushDefault, use the system global SysDirection
  809.     if (alignment == weFlushDefault) 
  810.     {
  811.         alignment = (GetSysDirection() == 0) ? weFlushLeft : weFlushRight;
  812.     }
  813.     if (alignment == weFlushRight) 
  814.     {
  815.         retval = slop;                            // right aligned
  816.     }
  817.     else if (alignment == weCenter) 
  818.     {
  819.         retval = slop / 2;                        // centered
  820.     }
  821.     else
  822.     {
  823.         retval = 0;                                // left aligned or justified
  824.     }
  825.     return retval;
  826. }
  827.  
  828. pascal void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *theEnvironment)
  829. {
  830.     GetPort(&theEnvironment->envPort);
  831.     SetPort(port);
  832.     GetPenState(&theEnvironment->envPen);
  833.     PenNormal();
  834.     theEnvironment->envStyle.tsFont = port->txFont;
  835.     theEnvironment->envStyle.tsFace = port->txFace;
  836.     theEnvironment->envStyle.tsFlags = saveColor;        // remember if color was saved
  837.     theEnvironment->envStyle.tsSize = port->txSize;
  838.     if (saveColor) 
  839.     {
  840.         GetForeColor(&theEnvironment->envStyle.tsColor);
  841.     }
  842.     theEnvironment->envMode = port->txMode;
  843.     TextMode(srcOr);
  844. }
  845.  
  846. pascal void _WERestoreQDEnvironment(const QDEnvironment *theEnvironment)
  847. {
  848.     SetPenState(&theEnvironment->envPen);
  849.     TextFont(theEnvironment->envStyle.tsFont);
  850.     TextFace(theEnvironment->envStyle.tsFace);
  851.     TextSize(theEnvironment->envStyle.tsSize);
  852.     TextMode(theEnvironment->envMode);
  853.     if (theEnvironment->envStyle.tsFlags) 
  854.     {
  855.         RGBForeColor(&theEnvironment->envStyle.tsColor);
  856.     }
  857.     SetPort(theEnvironment->envPort);
  858. }
  859.  
  860. pascal void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  861. {
  862.     // given a WERunAttributes record, fill in the runHeight, runAscent fields etc.
  863.     FontInfo fInfo;
  864.     QDEnvironment saveEnvironment;
  865.  
  866.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  867.  
  868.     // we don't want a zero font size; although QuickDraw accepts zero to mean
  869.     // the default font size, it can cause trouble to us when we do calculations
  870.     if (targetStyle->runStyle.tsSize == 0) 
  871.         targetStyle->runStyle.tsSize = 12;
  872.  
  873.     // set the text attributes
  874.     TextFont(targetStyle->runStyle.tsFont);
  875.     TextSize(targetStyle->runStyle.tsSize);
  876.     TextFace(targetStyle->runStyle.tsFace);
  877.     GetFontInfo(&fInfo);
  878.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  879.     targetStyle->runAscent = fInfo.ascent;
  880.  
  881.     _WERestoreQDEnvironment(&saveEnvironment);
  882. }
  883.  
  884. pascal void _WECopyStyle(const WETextStyle *sourceStyle, WETextStyle *targetStyle,
  885.                         short offStyles, short mode)
  886. {
  887.     // Copy some or all of the attributes composing sourceStyle to targetStyle.
  888.     // The mode parameter determines which attributes are to be copied and how.
  889.     // If mode contains weDoToggleFace,  offStyles indicates which
  890.     // QuickDraw styles are to be turned off.
  891.  
  892.     // COPY FONT
  893.     if (mode & weDoFont)
  894.     { 
  895.             targetStyle->tsFont = sourceStyle->tsFont;
  896. #if WASTE_RESOLVE_FONT_DESIGNATORS
  897.             if (targetStyle->tsFont == systemFont)
  898.                 targetStyle->tsFont = GetSysFont();
  899.             if (targetStyle->tsFont == applFont)
  900.                 targetStyle->tsFont = GetAppFont();
  901. #endif
  902.     }
  903.     
  904.     // COPY SIZE
  905.     if (mode & (weDoSize | weDoAddSize))
  906.     {
  907.         // copy size to a long variable to avoid integer overflows when doing additions
  908.         long longSize = sourceStyle->tsSize;
  909.  
  910.         // zero really means 12
  911.         if (longSize == 0)
  912.             longSize = 12;
  913.  
  914.         // if kModeAddSize is set, the source size is added to the target size,
  915.         // otherwise the source size replaces the target size outright
  916.         if (mode & weDoAddSize) 
  917.         {
  918.             longSize += targetStyle->tsSize;
  919.         }
  920.         // range-check the resulting size
  921.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  922.         targetStyle->tsSize = longSize;
  923.     } // if alter size
  924.     
  925.     // COPY FACE
  926.     if (mode & (weDoFace | weDoFaceMask))
  927.     {
  928.         long sourceFace = sourceStyle->tsFace;
  929.         long targetFace = targetStyle->tsFace;
  930.         
  931.         if (mode & weDoFaceMask)
  932.         {
  933.             // USE MASK
  934.             // if kModeFaceMask is set, copy the QuickDraw styles (tsFace field)
  935.             // using the tsFlags field as a mask specifying which bits in the tsFace
  936.             // field are to be copied.
  937.             long sourceMask = sourceStyle->tsFlags;
  938.             targetFace = (sourceFace & sourceMask) | (targetFace & (~sourceMask));
  939.         }
  940.         else {
  941.             // IGNORE MASK
  942.             // sourceFace replaces targetFace outright if one or both of these conditions hold:
  943.             // 1. sourceFace is zero (= empty set = plain text)
  944.             // 2. the kModeReplaceFace bit is set
  945.     
  946.             if ((sourceFace == 0) || (mode & weDoReplaceFace))
  947.             {
  948.                 targetFace = sourceFace;
  949.             }
  950.             else {
  951.                 // Otherwise sourceFace is interpreted as a bitmap indicating
  952.                 // which styles are to be altered -- all other styles are left intact.
  953.                 // What exactly happens to the styles indicated in sourceFace
  954.                 // depends on whether the kModeToggleFace bit is set or clear.
  955.         
  956.                 // if kModeToggleFace is set, turn a style off if it's set in offStyles,
  957.                 // else turn it on
  958.                 if (mode & weDoToggleFace) 
  959.                 {
  960.                     targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  961.                 }
  962.                 else
  963.                 {
  964.                     // if kModeToggleFace is clear, turn on the styles specified in sourceStyle
  965.                     targetFace |= sourceFace;
  966.                 }
  967.             }
  968.         }
  969.  
  970.         // the condense and extend attributes are mutually exclusive: if one is set
  971.         // in sourceFace, remove it from targetFace
  972.         if (targetFace & condense)
  973.         {
  974.             targetFace &= (~extend);
  975.         }
  976.         else if (targetFace & extend)
  977.         {
  978.             targetFace &= (~condense);
  979.         }
  980.         
  981.         targetStyle->tsFace = targetFace;
  982.     }
  983.  
  984.     // COPY COLOR
  985.     if (mode & weDoColor)
  986.     {
  987.         targetStyle->tsColor = sourceStyle->tsColor;
  988.     }
  989.  
  990. #if WASTE_OBJECTS
  991.     // if kModeObject is set, copy object descriptor
  992.     if (mode & weDoObject)
  993.     {
  994.         targetStyle->tsObject = sourceStyle->tsObject;
  995.     }
  996. #endif
  997.  
  998.     // always clear targetStyle->tsFlags by default
  999.     targetStyle->tsFlags = 0;
  1000.  
  1001.     // if kModeFlags is set, copy the tsFlags field
  1002.     if (mode & weDoFlags)
  1003.     { 
  1004.         targetStyle->tsFlags = sourceStyle->tsFlags;
  1005.     }
  1006. }
  1007.  
  1008. pascal Boolean _WEOffsetInRange(long offset, char edge, long rangeStart, long rangeEnd)
  1009. {
  1010.     // return TRUE if the position specified by the pair < offset, edge >
  1011.     // is within the specified range
  1012.  
  1013.     // if edge is kTrailingEdge, offset really refers to the preceding character
  1014.     if (edge == kTrailingEdge) 
  1015.     {
  1016.         offset--;
  1017.     }
  1018.     // return TRUE iff offset is within the specified range
  1019.     return ((offset >= rangeStart) && (offset < rangeEnd));
  1020. }
  1021.  
  1022.  
  1023.  
  1024.